package com.huangfu.learning.javabase.java.pattern.action.memento;

import java.util.Vector;

/**
 * Created with IntelliJ IDEA.
 * Author: huangfu.daohui
 * Date: 2018/8/11
 * Time: 14:35
 * Version: 1.0
 * Description:备忘录模式：又叫做快照模式(Snapshot Pattern)或Token模式，保存一个对象的某个状态，以便在适当的时候恢复对象。
 * <p>
 * 参考：https://blog.csdn.net/u012124438/article/details/70473553
 * https://blog.csdn.net/o279642707/article/details/60767258
 * <p>
 * 关键代码：客户不与备忘录类耦合，与备忘录管理类耦合。
 * 三个角色：备忘录(Memento)角色、发起人(Originator)角色、负责人(Caretaker)角色。
 * <p>
 * 应用实例： 1、后悔药。
 * 2、打游戏时的存档。
 * 3、Windows 里的 ctri + z。
 * 4、IE 中的后退。
 * 4、数据库的事务管理。
 * <p>
 * 优点： 1、给用户提供了一种可以恢复状态的机制，可以使用户能够比较方便地回到某个历史的状态。
 * 2、实现了信息的封装，使得用户不需要关心状态的保存细节。
 * <p>
 * 缺点：消耗资源。如果类的成员变量过多，势必会占用比较大的资源，而且每一次保存都会消耗一定的内存。
 **/
public class MementoPattern {

    // 测试
    public static void main(String[] args) {
        whiteTest(); // 白盒测试
        blackTest(); // 黑盒测试
    }


    // 白盒测试
    public static void blackTest() {
        // 发起者
        Originator originator = new Originator();
        // 责任人
        Caretaker caretaker = new Caretaker();
        //改变发起人的状态
        originator.setState("on");
        originator.getState();
        //创建备忘录对象，并将发起人对象的状态存储起来
        caretaker.saveMemento(originator.createMemento());

        //再次改变发起人对象的状态
        originator.setState("off");
        originator.getState();

        //恢复发起人对象的状态
        originator.restoreMemento(caretaker.retrieveMemento());
        originator.getState();
    }

    // 黑盒测试
    public static void whiteTest() {

        Originator2 o = new Originator2();
        Caretaker2 c = new Caretaker2(o);

        //改变发起人的状态
        o.setState("state0");

        //创建一个检查点
        c.createMemento();
        o.setState("state1");
        c.createMemento();
        o.setState("state2");
        c.createMemento();
        o.setState("state3");
        c.createMemento();
        o.setState("state4");
        c.createMemento();

        //打印出所有状态
        o.printStates();

        //恢复到第3个检查点
        System.out.println("Restoring to 3");
        c.reStoreMemento(3);
        o.printStates();

        //恢复到第0个检查点
        System.out.println("Restoring to 0");
        c.reStoreMemento(0);
        o.printStates();

        //恢复到第4个检查点
        System.out.println("Restoring to 4");
        c.reStoreMemento(4);
        o.printStates();
    }
}

/**
 * “白箱”备忘录模式
 */
// 备忘录类
class Memento {

    private String state;

    public Memento(String state) {
        this.state = state;
    }

    public String getState() {
        return state;
    }

    public void setState(String state) {
        this.state = state;
    }
}


/**
 * 发起人
 */
class Originator {

    private String state;

    //工厂方法，返回一个新的备忘录对象
    public Memento createMemento() {
        return new Memento(state);
    }

    //将发起人恢复到备忘录对象所记载的状态
    public void restoreMemento(Memento memento) {
        this.state = memento.getState();
    }

    public String getState() {
        System.out.println("Current state:" + state);
        return state;
    }

    public void setState(String state) {
        this.state = state;
    }
}

/**
 * 责任人
 */
class Caretaker {

    Memento memento;

    //恢复备忘录
    public Memento retrieveMemento() {
        return this.memento;
    }

    //保存备忘录
    public void saveMemento(Memento memento) {
        this.memento = memento;
    }
}

/**
 * 黑箱”备忘录模式
 * “黑箱”备忘录模式相比“白箱”备忘录模式有如下区别：
 * 1.将Memento设成Originator类的内部类；
 * 2.将Memento的方法全部设成私有方法，这样只有它自己和发起人Originator可以调用；
 * 3.在外部提供一个标识接口MementoIF给Caretaker以及其他对象，
 * 标识接口MementoIF没有提供任何方法，因此对外部来说Memento对象的内容都是不可见的。
 */
//标识接口（窄接口）
interface MementoIF {

}

class Originator2 {

    private Vector<Object> states;

    private int index;

    public Originator2() {
        states = new Vector<Object>();
        index = 0;
    }

    //工厂方法，返回一个新的备忘录对象
    public MementoIF createMemento() {
        return new Memento(this.states, index);
    }


    //将发起人恢复到备忘录对象所记载的状态
    public void restoreMemento(MementoIF memento) {
        states = ((Memento) memento).getStates();
        index = ((Memento) memento).getIndex();
    }


    //状态的赋值方法
    public void setState(Object state) {
        this.states.addElement(state);
        index++;
    }

    //辅助方法，打印出所有状态
    public void printStates() {

        System.out.println("Total number of states:" + index);
        for (Object o : states) {
            System.out.println(o.toString());
        }
    }
    //内部类
    protected class Memento implements MementoIF {

        private Vector<Object> saveStates;

        private int saveIndex;

        @SuppressWarnings("unchecked")
        //_states一定是Vector<Object类型的变量，复制后也一定是Vector<Object的变量
        private Memento(Vector<Object> _states, int _index) {
            //保存客户端传来的状态对象的拷贝，否则客户端的修改会影响到保存的状态。
            saveStates = (Vector<Object>) _states.clone();
            saveIndex = _index;
        }

        private Vector<Object> getStates() {
            return saveStates;
        }

        private int getIndex() {
            return saveIndex;
        }
    }
}

class Caretaker2 {
    private Originator2 o;

    private Vector<MementoIF> mementos = new Vector<MementoIF>();

    private int currentIndex;

    public Caretaker2(Originator2 o) {
        this.o = o;
        currentIndex = 0;
    }

    //创建一个新的检查点
    public void createMemento() {
        mementos.addElement(o.createMemento());
        currentIndex++;
    }


    //将发起人恢复到某个检查点
    public void reStoreMemento(int index) {
        o.restoreMemento(mementos.elementAt(index));
    }

    //删除某个检查点
    public void removeMemento(int index) {
        mementos.removeElementAt(index);
    }

}